//package com.hmdp.utils;
//
//import cn.hutool.core.util.StrUtil;
//import cn.hutool.json.JSONObject;
//import cn.hutool.json.JSONUtil;
//import com.hmdp.dto.Result;
//import com.hmdp.entity.Shop;
//import lombok.AllArgsConstructor;
//import lombok.Data;
//import lombok.extern.slf4j.Slf4j;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.data.redis.core.StringRedisTemplate;
//import org.springframework.stereotype.Component;
//import org.springframework.transaction.annotation.Transactional;
//
//import java.time.LocalDateTime;
//import java.util.concurrent.ExecutorService;
//import java.util.concurrent.Executors;
//import java.util.concurrent.TimeUnit;
//import java.util.function.Function;
//
///**
// * @ClassName CacheClient
// * @Description TODO
// * @Author kiku
// * @Date 2025/2/9 16:53
// * @Version 1.0
// */
//@Component
//@Slf4j
//@AllArgsConstructor
//public class CacheClient {
//    @Autowired
//    private StringRedisTemplate redisTemplate;
//
//    public void set(String key, Object value, Long time, TimeUnit timeUnit) {
//        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,timeUnit);
//
//    }
//    public void setWithLogicExpire(String key, Object value, Long time, TimeUnit timeUnit) {
//        //设置逻辑过期
//        RedisData redisData = new RedisData();
//        redisData.setExpireTime(LocalDateTime.now().plusSeconds(timeUnit.toSeconds(time)));
//        redisData.setData(value);
//        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));
//    }
//
//
//    public Shop queryWithMutex(Long id) throws InterruptedException {
//        String lockKey = RedisConstants.LOCK_SHOP_KEY + id;
//
//        //先从redis查缓存，不存在查数据库
//        String key = RedisConstants.CACHE_SHOP_KEY + id;
//        String shop = redisTemplate.opsForValue().get(key);
//        if (StrUtil.isNotBlank(shop)) {
//            Shop shopRes = JSONUtil.toBean(shop, Shop.class);
//            return shopRes;
//        }
//        //空值
//        if (shop != null) {
//            return null;
//        }
//        //实现缓存重建
//        //获取互斥锁
//        boolean flag = tryLock(lockKey);
//        if (!flag) {
//            //获取失败就休眠并重试
//            Thread.sleep(5);
//            //重试就采用递归执行即可
//            return queryWithMutex(id);
//        }
//        //获取成功就进行数据库查询并将数据写入redis
//        Shop shopRes = getById(id);
//        if (shopRes == null) {
//            //将空值写入redis
//            redisTemplate.opsForValue().set(key, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);
//            return null;
//        }
//        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shopRes), RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);
//        unlock(lockKey);
//        return shopRes;
//    }
//
//
//    @Transactional
//    public Result update(Shop shop) {
//        //先操作数据库后删除缓存
//        updateById(shop);
//        if (shop.getId() == null) {
//            return Result.fail("店铺id为null");
//        }
//        String key = RedisConstants.CACHE_SHOP_KEY + shop.getId();
//        redisTemplate.delete(key);
//
//        return Result.ok();
//    }
//
//    /**
//     * 基于redis setnx实现锁
//     */
//    private boolean tryLock(String key) {
//        Boolean flag = redisTemplate.opsForValue().setIfAbsent("lock", "1", 10, TimeUnit.SECONDS);
//        return flag;
//    }
//
//    private void unlock(String key) {
//        redisTemplate.delete(key);
//    }
//
//    public <R,ID> R queryWithPassThrough(String prefix, ID id, Class<R> type, Function<ID,R> dbFallback, Long time, TimeUnit timeUnit) {
//        //先从redis查缓存，不存在查数据库
//        String key = prefix + id;
//        String json = redisTemplate.opsForValue().get(key);
//        if (StrUtil.isNotBlank(json)) {
//            return JSONUtil.toBean(json, type);
//        }
//        //空值
//        if (json != null) {
//            return null;
//        }
//        R r = dbFallback.apply(id);
//        if (r == null) {
//            //将空值写入redis
//            redisTemplate.opsForValue().set(key, "", 10, timeUnit);
//            return null;
//        }
//        this.set(key, r, time, timeUnit);
//        return r;
//    }
//
//    //线程池
//    private static final ExecutorService CACHE_REBUILD_EXECUTORS = Executors.newFixedThreadPool(10);
//
//    /**
//     * 逻辑过期解决缓存击穿
//     * @param id
//     */
//    public Shop queryWithLogicExpire(Long id) {
//        //先从redis查缓存，不存在查数据库
//        String key = RedisConstants.CACHE_SHOP_KEY + id;
//        String shop = redisTemplate.opsForValue().get(key);
//        if (StrUtil.isBlank(shop)) {
//            return null;
//        }
//        //命中就进行如下操作
//        //反序列化判断是否进行过期
//        RedisData redisData = JSONUtil.toBean(shop, RedisData.class);
//        LocalDateTime expireTime = redisData.getExpireTime();
//        JSONObject data = (JSONObject) redisData.getData();
//        Shop shopRes = JSONUtil.toBean(data, Shop.class);
//        //数据有效就直接返回
//        if (expireTime.isAfter(LocalDateTime.now())) {
//            return shopRes;
//        }
//        //数据无效就开新线程重建缓存
//        String lockKey = RedisConstants.LOCK_SHOP_KEY + id;
//        boolean isLock = tryLock(lockKey);
//        if (isLock) {
//            //获取成功就开新线程重建缓存
//            CACHE_REBUILD_EXECUTORS.submit(
//                    () -> {
//                        try {
//                            //重建缓存
//                            this.saveShop2Redis(id, 20L);
//                        } catch (Exception e) {
//                            throw new RuntimeException(e);
//                        }finally {
//                            //释放锁
//                            unlock(lockKey);
//                        }
//                    }
//            );
//        }
//        //返回过期的店铺信息
//        return shopRes;
//    }
//    public void saveShop2Redis(Long id, Long seconds) {
//        //查询店铺数据
//        Shop shop = getById(id);
//
//        //封装逻辑过期时间
//        RedisData redisData = new RedisData();
//        redisData.setExpireTime(LocalDateTime.now().plusSeconds(seconds));
//        redisData.setData(shop);
//        //写入redis
//        redisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(redisData));
//
//    }
//}
